package com.buaa.aimall.Utils;

import com.buaa.aimall.Utils.Config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import lombok.SneakyThrows;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.minio.ListObjectsArgs;
import io.minio.Result;
import io.minio.messages.Item;

@Component
public class MinioUtils {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinioConfig configuration;

    private void handleError(Exception e) {
        e.printStackTrace();
        // 可以在这里加入日志记录或其他处理逻辑
    }

    public boolean existBucket(String name) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
                exists = true;
            }
            return exists;
        } catch (Exception e) {
            handleError(e);
            return false;
        }
    }

    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            return true;
        } catch (Exception e) {
            handleError(e);
            return false;
        }
    }

    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            return true;
        } catch (Exception e) {
            handleError(e);
            return false;
        }
    }

    @SneakyThrows
    public Map getPolicy(String fileName, ZonedDateTime time) {
        try {
            PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
            postPolicy.addEqualsCondition("key", fileName);
            Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
            HashMap<String, String> map1 = new HashMap<>();
            map.forEach((k, v) -> {
                map1.put(k.replaceAll("-", ""), v);
            });
            map1.put("host", configuration.getUrl() + "/" + configuration.getBucketName());
            return map1;
        } catch (Exception e) {
            handleError(e);
            return null;
        }
    }

    /**
     * @param objectName 对象名称
     * @param method     方法
     * @param time       时间
     * @param timeUnit   时间单位
     * @Description 获取上传文件的url
     */
    public String getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(configuration.getBucketName())
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (Exception e) {
            handleError(e);
            return null;
        }
    }

    /**
     * @param file     文件
     * @param fileName 文件名称
     * @Description 上传文件
     */
    public void upload(MultipartFile file, String fileName) {
        try {
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(fileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
            inputStream.close();
        } catch (Exception e) {
            handleError(e);
        }
    }

    /**
     * @param objectName 对象名称
     * @param time       时间
     * @param timeUnit   时间单位
     * @Description 根据filename获取文件访问地址
     */
    public String getUrl(String objectName, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(configuration.getBucketName())
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (Exception e) {
            handleError(e);
            return null;
        }
    }

    /**
     * @Description description: 下载文件
     */
    public ResponseEntity<byte[]> download(String fileName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                handleError(e);
            }
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<>(bytes, headers, HttpStatus.SUCCESS);
        } catch (Exception e) {
            handleError(e);
        } finally {
            try {
                if (in != null) in.close();
                if (out != null) out.close();
            } catch (IOException e) {
                handleError(e);
            }
        }
        return responseEntity;
    }

    /**
     * @param objectFile 对象文件
     */
    public String getFileUrl(String objectFile) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(configuration.getBucketName())
                    .object(objectFile)
                    .build());
        } catch (Exception e) {
            handleError(e);
            return null;
        }
    }

    /**
     * 列出指定前缀（文件夹）下的所有文件
     * @param prefix 前缀（文件夹路径）
     * @return 文件列表
     */
    public List<Item> listObjects(String prefix) {
        List<Item> items = new ArrayList<>();
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                    .bucket(configuration.getBucketName())
                    .prefix(prefix)
                    .recursive(true)
                    .build()
            );
            
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            handleError(e);
            throw new RuntimeException("列出文件失败: " + e.getMessage());
        }
        return items;
    }

    /**
     * 修改文件内容
     * @param fileName 要修改的文件名
     * @param newFile 新的文件内容
     * @return 是否修改成功
     */
    public boolean updateFile(String fileName, MultipartFile newFile,String newFileName) {
        try {
            // 检查文件是否存在
            StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(fileName)
                    .build());
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(fileName)
                    .build());
            upload(newFile, newFileName);
            return true;
        } catch (Exception e) {
            System.err.println("文件更新失败，原因: " + e.getMessage());
            e.printStackTrace();
            handleError(e);
            return false;
        }
    }
}
